﻿using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Xml.Linq;
using SharePointUtilitiesForDevelopers.Repository;
using System.Linq;

namespace SharePointUtilitiesForDevelopers.Actions
{
    /// <summary>
    /// A form for retrieving list items.
    /// </summary>
    public partial class ListItemsForm : Form
    {
        #region Fields

        private readonly ISharePointListRepository _listRepository;
        private readonly ISharePointWebRepository _webRepository;
        private string _querySnapshot = string.Empty;

        #endregion Fields

        #region Constructors 
        
        /// <summary>
        /// Initializes a new instance of the <see cref="ListItemsForm"/> class.
        /// </summary>
        /// <param name="listRepository">The list repository.</param>
        /// <param name="webRepository">The web repository.</param>
        public ListItemsForm(ISharePointListRepository listRepository, ISharePointWebRepository webRepository)
        {
            InitializeComponent();

            // Get the dependencies
            _listRepository = listRepository;
            _webRepository = webRepository;

        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Gets or sets the web.
        /// </summary>
        /// <value>The web.</value>
        public SharePointWeb Web { get; set; }

        /// <summary>
        /// Gets or sets the list.
        /// </summary>
        /// <value>The list.</value>
        public SharePointList List { get; set; }

        /// <summary>
        /// Gets or sets the view.
        /// </summary>
        /// <value>The view.</value>
        public SharePointListView View { get; set; }
        
        #endregion Properties

        #region Event Handlers

        /// <summary>
        /// Handles the Load event of the ListItemsForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void ListItemsForm_Load(object sender, EventArgs e)
        {
            try
            {
                // Show wait
                Cursor = Cursors.WaitCursor;

                // Bind the lists
                BindLists();

                // Bind the query options
                BindQueryOptions();

                // Set the selected list
                if (List != null)
                {
                    listComboBox.SelectedItem = List;
                }

                // Set selected view
                if (View != null)
                {
                    viewComboBox.SelectedItem = View;
                }
                
                // Reset the status bar
                ResetStatusBar();

            }
            finally
            {
                // Show default
                Cursor = Cursors.Default;
            }
        }
        
        /// <summary>
        /// Handles the Click event of the getListItemsButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void getListItemsButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Show wait
                Cursor = Cursors.WaitCursor;
                getListItemsButton.Enabled = false;

                // Get list items
                GetListItems();
            }
            finally
            {
                // Show default
                getListItemsButton.Enabled = true;
                Cursor = Cursors.Default;
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the listComboBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void listComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                // Show wait
                Cursor = Cursors.WaitCursor;

                // Bind the views
                BindListViews();

                // Clear errors
                errorProvider.Clear();
            }
            finally
            {
                // Show default
                Cursor = Cursors.Default;
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the viewComboBox control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void viewComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                // Show wait
                Cursor = Cursors.WaitCursor;

                // Bind the query
                BindQuery();

                // Bind the view fields
                BindViewFields();

                // Bind row limit
                BindRowLimit();

                // Clear errors
                errorProvider.Clear();
            }
            finally
            {
                // Show default
                Cursor = Cursors.Default;
            }
        }

        /// <summary>
        /// Handles the Click event of the viewFieldsBuildButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void viewFieldsBuildButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Show wait
                Cursor = Cursors.WaitCursor;

                // Validate xml
                if (ValidateViewFieldsXml(false))
                {
                    // Get the view fields text as xml
                    var viewFieldsXml = GetViewFieldsTextAsXml();

                    // Create the form
                    var buildVwFieldsForm = new BuildViewFieldsForm { List = List, Web = Web, ViewFieldsDefinition = viewFieldsXml };

                    // Show the build form
                    var result = buildVwFieldsForm.ShowDialog(this);

                    // If the user accepted changes
                    if (result == DialogResult.OK)
                    {
                        // Update the view fields xml text box
                        viewFieldsTextBox.Text = string.Empty;
                        if (buildVwFieldsForm.ViewFieldsDefinition != null)
                        {
                            viewFieldsTextBox.Text = buildVwFieldsForm.ViewFieldsDefinition.ToString();
                        }
                    }
                }
            }
            finally
            {
                // Show default
                Cursor = Cursors.Default;
            }

        }

        /// <summary>
        /// Handles the Click event of the viewFieldsValidateButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void viewFieldsValidateButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Show wait
                Cursor = Cursors.WaitCursor;

                // Validate xml
                ValidateViewFieldsXml(true);

            }
            finally
            {
                // Show default
                Cursor = Cursors.Default;
            }
        }

        /// <summary>
        /// Handles the Click event of the viewFieldsCopyButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void viewFieldsCopyButton_Click(object sender, EventArgs e)
        {
            Clipboard.SetDataObject(viewFieldsTextBox.Text);
        }

        /// <summary>
        /// Handles the Click event of the queryCopyButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void queryCopyButton_Click(object sender, EventArgs e)
        {
            Clipboard.SetDataObject(queryTextBox.Text);
        }

        /// <summary>
        /// Handles the Click event of the queryOptionsCopyButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void queryOptionsCopyButton_Click(object sender, EventArgs e)
        {
            Clipboard.SetDataObject(queryOptionsTextBox.Text);
        }

        /// <summary>
        /// Handles the Click event of the queryOptionsValidateButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void queryOptionsValidateButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Show wait
                Cursor = Cursors.WaitCursor;

                // Validate xml
                ValidateQueryOptionsXml(true);

            }
            finally
            {
                // Show default
                Cursor = Cursors.Default;
            }
        }
        
        /// <summary>
        /// Handles the Click event of the queryValidateButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void queryValidateButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Show wait
                Cursor = Cursors.WaitCursor;

                // Validate xml
                ValidateQueryXml(true);

            }
            finally
            {
                // Show default
                Cursor = Cursors.Default;
            }
        }

        /// <summary>
        /// Handles the Click event of the queryBuildButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void queryBuildButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Show wait
                Cursor = Cursors.WaitCursor;

                // Validate xml
                if (ValidateQueryXml(false))
                {
                    //// Get the query text as xml
                    //var queryXml = GetQueryTextAsXml();

                    //// Create the form
                    //var buildQueryForm = new BuildQueryForm { List = List, Web = Web, QueryDefinition = queryXml };

                    //// Show the build form
                    //var result = buildQueryForm.ShowDialog(this);

                    // If the user accepted changes
                    //if (result == DialogResult.OK)
                    //{
                    //    // Update the view fields xml text box
                    //    queryTextBox.Text = string.Empty;
                    //    if (buildQueryForm.QueryDefinition != null)
                    //    {
                    //        queryTextBox.Text = buildQueryForm.QueryDefinition.ToString();
                    //    }
                    //}
                }

            }
            finally
            {
                // Show default
                Cursor = Cursors.Default;
            }

        }

        /// <summary>
        /// Handles the Click event of the queryOptionsBuildButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void queryOptionsBuildButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Show wait
                Cursor = Cursors.WaitCursor;

                // Validate xml
                if (ValidateQueryOptionsXml(false))
                {
                    // Get the view fields text as xml
                    var queryOptionsXml = GetQueryOptionsTextAsXml();

                    // Create the form
                    var buildQueryOptionsForm = new BuildQueryOptionsForm { QueryOptionsDefinition = queryOptionsXml };

                    // Show the build form
                    var result = buildQueryOptionsForm.ShowDialog(this);

                    // If the user accepted changes
                    if (result == DialogResult.OK)
                    {
                        // Update the view fields xml text box
                        queryOptionsTextBox.Text = string.Empty;
                        if (buildQueryOptionsForm.QueryOptionsDefinition != null)
                        {
                            queryOptionsTextBox.Text = buildQueryOptionsForm.QueryOptionsDefinition.ToString();
                        }
                    }
                }
            }
            finally
            {
                // Show default
                Cursor = Cursors.Default;
            }

        }

        /// <summary>
        /// Handles the HelpRequested event of the ListItemsForm control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="hlpevent">The <see cref="System.Windows.Forms.HelpEventArgs"/> instance containing the event data.</param>
        private void ListItemsForm_HelpRequested(object sender, HelpEventArgs hlpevent)
        {
            Help.ShowHelp(this, string.Format(Properties.Resources.HelpSearchUrl, Properties.Resources.HelpKeywordGetListItems));
        }

        #endregion Event Handlers

        #region Helper Methods

        /// <summary>
        /// Resets the status bar.
        /// </summary>
        private void ResetStatusBar()
        {
            // Init status labels
            querySearchStatusLabel.Text = string.Empty;
            queryResultsTimeLabel.Text = string.Empty;
            queryResultsRowCount.Text = string.Empty;
        }

        /// <summary>
        /// Validates the view fields XML.
        /// </summary>
        /// <param name="showMessageIfValid">Shows a message if the xml is valid.</param>
        private bool ValidateViewFieldsXml(bool showMessageIfValid)
        {
            try
            {
                GetViewFieldsTextAsXml();

                if (showMessageIfValid)
                {
                    MessageBox.Show(
                        this, 
                        Properties.Resources.ValidationViewFieldsFormatValid,
                        Properties.Resources.ValidationViewFieldsCaption, 
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
                }

                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    this, 
                    string.Format(Properties.Resources.ValidationViewFieldsFormat, ex), 
                    Properties.Resources.ValidationViewFieldsCaption, 
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                return false;
            }
        }

        /// <summary>
        /// Validates the query XML.
        /// </summary>
        /// <param name="showMessageIfValid">Shows a message if the xml is valid.</param>
        private bool ValidateQueryXml(bool showMessageIfValid)
        {
            try
            {
                GetQueryTextAsXml();

                if (showMessageIfValid)
                {
                    MessageBox.Show(
                        this, 
                        Properties.Resources.ValidationQueryFormatValid,
                        Properties.Resources.ValidationQueryCaption, 
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
                }

                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    this, 
                    string.Format(Properties.Resources.ValidationQueryFormat, ex), 
                    Properties.Resources.ValidationQueryCaption, 
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                return false;
            }
        }

        /// <summary>
        /// Validates the query options XML.
        /// </summary>
        /// <param name="showMessageIfValid">Shows a message if the xml is valid.</param>
        private bool ValidateQueryOptionsXml(bool showMessageIfValid)
        {
            try
            {
                GetQueryOptionsTextAsXml();

                if (showMessageIfValid)
                {
                    MessageBox.Show(
                        this, 
                        Properties.Resources.ValidationQueryOptionsFormatValid,
                        Properties.Resources.ValidationQueryOptionsCaption, 
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
                }

                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    this, 
                    string.Format(Properties.Resources.ValidationQueryOptionsFormat, ex.Message), 
                    Properties.Resources.ValidationQueryOptionsCaption, 
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                return false;
            }
        }

        /// <summary>
        /// Gets the view fields.
        /// </summary>
        /// <returns>An <see cref="XElement"/>.</returns>
        private XElement GetViewFieldsTextAsXml()
        {
            if (!string.IsNullOrEmpty(viewFieldsTextBox.Text))
            {
                return XElement.Parse(viewFieldsTextBox.Text);
            }
            return null;
        }

        /// <summary>
        /// Gets the query.
        /// </summary>
        /// <returns>An <see cref="XElement"/>.</returns>
        private XElement GetQueryTextAsXml()
        {
            if (!string.IsNullOrEmpty(queryTextBox.Text))
            {
                return XElement.Parse(queryTextBox.Text);
            }
            return null;
        }


        /// <summary>
        /// Gets the query options.
        /// </summary>
        /// <returns>An <see cref="XElement"/>.</returns>
        private XElement GetQueryOptionsTextAsXml()
        {
            if (!string.IsNullOrEmpty(queryOptionsTextBox.Text))
            {
                return XElement.Parse(queryOptionsTextBox.Text);
            }
            return null;
        }

        /// <summary>
        /// Validates the query.
        /// </summary>
        /// <returns></returns>
        private bool ValidateForm()
        {
            // Clear errors
            errorProvider.Clear();
            var isValid = true;

            // Query
            try
            {
                GetQueryTextAsXml();
            }
            catch
            {
                errorProvider.SetError(queryLabel, Properties.Resources.ValidationQueryFormat);
                isValid = false;
            }
            
            // Query Options
            try
            {
                GetQueryOptionsTextAsXml();
            }
            catch
            {
                errorProvider.SetError(queryOptionsLabel, Properties.Resources.ValidationQueryOptionsFormat);
                isValid = false;
            }

            // View Fields
            try
            {
                GetViewFieldsTextAsXml();
            }
            catch
            {
                errorProvider.SetError(viewFieldsLabel, Properties.Resources.ValidationViewFieldsFormat);
                isValid = false;
            }

            // Row Limit
            if(!string.IsNullOrEmpty(rowLimitTextBox.Text))
            {
                int rowLimit;
                if (!int.TryParse(rowLimitTextBox.Text, out rowLimit))
                {
                    errorProvider.SetError(rowLimitTextBox, Properties.Resources.ValidationRowLimitFormat);
                    isValid = false;
                }
            }

            return isValid;
        }

        /// <summary>
        /// Gets the list items.
        /// </summary>
        private void GetListItems()
        {
            var watch = new Stopwatch();
            try
            {
                // Validate the form
                if (!ValidateForm())
                {
                    return;
                }

                // Start stopwatch
                watch.Start();

                // Set status label
                queryResultsRowCount.Text = string.Empty;
                querySearchStatusLabel.Text = Properties.Resources.GetListItemsSearchStatusLabelExecuting;

                // Get the query parameters
                var listName = listComboBox.SelectedValue as string;
                var viewName = viewComboBox.SelectedValue as string;
                int rowLimitParsed;
                int? rowLimit = null;
                if (int.TryParse(rowLimitTextBox.Text, out rowLimitParsed))
                {
                    rowLimit = rowLimitParsed;
                }
                
                // Get the list items
                var result = _listRepository.GetListItems(
                    Web.Url, 
                    listName, 
                    viewName, 
                    queryTextBox.Text,
                    viewFieldsTextBox.Text, 
                    rowLimit,
                    queryOptionsTextBox.Text, 
                    null);

                // Convert the results to a datatable
                listsItemsDataGridView.DataSource = result.ToDataTable();

                // Update row count status
                queryResultsRowCount.Text = string.Format(Properties.Resources.GetListItemsRowCountFormatString, result.Count());
            }
            finally
            {
                // Update status
                querySearchStatusLabel.Text = Properties.Resources.GetListItemsSearchStatusLabelCompleted;
                queryResultsTimeLabel.Text = string.Format(Properties.Resources.GetListItemsSearchElapsedFormatString, watch.Elapsed);
            }
        }

        /// <summary>
        /// Binds the view fields.
        /// </summary>
        private void BindViewFields()
        {
            // Clear view fields
            viewFieldsTextBox.Text = string.Empty;

            // Get the selected view
            var view = viewComboBox.SelectedItem as SharePointListView;

            // If there is a view, clear the view fields
            if (view != null && view.ViewFieldsDefinition != null)
            {
                viewFieldsTextBox.Text = view.ViewFieldsDefinition.ToString();
            }
        }

        /// <summary>
        /// Binds the query options.
        /// </summary>
        private void BindQueryOptions()
        {
            // Query options don't change per-list, so just generate a default definition
            queryOptionsTextBox.Text = new SharePointQueryOptions().ToQueryOptionsXElement().ToString();
        }

        /// <summary>
        /// Binds the query.
        /// </summary>
        private void BindQuery()
        {

            // If the user customized the query, prompt them before replacing it
            if (_querySnapshot != queryTextBox.Text)
            {
                var result = MessageBox.Show(
                    this, 
                    Properties.Resources.GetListItemsQueryReplacePrompt,
                    Properties.Resources.GetListItemsQueryReplaceCaption,
                    MessageBoxButtons.YesNoCancel, 
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button3);

                if (result != DialogResult.Yes)
                {
                    _querySnapshot = queryTextBox.Text;
                    return;
                }
            }

            // Clear query
            queryTextBox.Text = string.Empty;

            // Get the selected view
            var view = viewComboBox.SelectedItem as SharePointListView;

            // Set the view query
            if (view != null && view.QueryDefinition != null)
            {
                queryTextBox.Text = view.QueryDefinition.ToString();
            }

            // Store a snapshot of the query so we can tell if the user changed it
            _querySnapshot = queryTextBox.Text;
        }

        /// <summary>
        /// Binds the row limit.
        /// </summary>
        private void BindRowLimit()
        {
            // Clear row limit
            rowLimitTextBox.Text = string.Empty;

            // Get the selected view
            var view = viewComboBox.SelectedItem as SharePointListView;

            // Set the view query
            if (view != null && view.RowLimit != null)
            {
                rowLimitTextBox.Text = view.RowLimit.ToString() ?? string.Empty;
            }
        }
        
        /// <summary>
        /// Binds the views.
        /// </summary>
        private void BindListViews()
        {

            // Clear views
            viewComboBox.DataSource = null;

            // Get the selected list
            var list = listComboBox.SelectedItem as SharePointList;

            if (list != null)
            {
                // Load views on demand
                if (list.Views == null && (!string.IsNullOrEmpty(list.Name)))
                {
                    _listRepository.LoadListViews(list, Web.Url);
                }

                if (list.Views != null)
                {
                    // Get the views to display
                    var views = list.Views.ToList();

                    // Bind the combo box
                    viewComboBox.BeginUpdate();
                    viewComboBox.DisplayMember = "FormattedName";
                    viewComboBox.ValueMember = "Name";
                    viewComboBox.DataSource = views;
                    viewComboBox.EndUpdate();

                    viewComboBox.SelectedItem = list.DefaultView;
                }
            }
        }

        /// <summary>
        /// Binds the lists.
        /// </summary>
        private void BindLists()
        {
            // Clear lists
            listComboBox.DataSource = null;

            // Load lists on demand
            if (Web.Lists == null)
            {
                _webRepository.LoadWebLists(Web);
            }

            if (Web.Lists != null)
            {
                // Get the lists to display
                var lists = Web.Lists/*.Where(l => !l.Hidden)*/.ToList();

                // Add a blank item
                lists.Insert(0, new SharePointList {Title = Properties.Resources.AllListsDropDownTitle});

                // Bind the combo box
                listComboBox.BeginUpdate();
                listComboBox.DisplayMember = "Title";
                listComboBox.ValueMember = "Id";
                listComboBox.DataSource = lists;
                listComboBox.EndUpdate();
            }
        }

        #endregion Helper Methods

    }
}